﻿/*
   Copyright (C) 2010-2011 Red Hat, Inc.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"
#include <glib.h>

#include <sys/stat.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#ifdef USE_SMARTCARD_012
#include <vscard_common.h>
#else
#ifdef USE_SMARTCARD
#include <libcacard.h>
#endif
#endif

#include "spice-widget.h"
#include "spice-gtk-session.h"
#include "spice-audio.h"
#include "spice-cmdline.h"
#include "spice-option.h"
#include "usb-device-widget.h"

#include "spicy-connect.h"
#include "spice-channel-priv.h"

/* add by gt */
#include "spicy.h"
#include "vcd-common-api/vcd-conf/vcd-conf.h"
#include "vcd-common-api/vcd-log.h"
#include "vcd-common-api/vcd-new-login-mode.h" 
#include "vcd-common-api/vcd-vm-status-control.h"
#include "vcd-common-api/vcd-get-vm-info.h"
#include "vcd-common-api/vcd-usb-redir.h"
#include "vcd-common-api/vcd-send-heartbeat.h"
#include "vcd-linux-ui/vcd-logined-window.h"
#include "vcd-linux-ui/vcd-message-dialog.h"
#include "vcd-common-api/vcd-ping.h"
#include "vcd-common-api/vcd-com-reconnect.h"
#include "vcd-common-api/vcd-download.h"
#include "vcd-common-api/vcd-json-parser.h"
#include "vcd-common-api/vcd-topbar/vcd-topbar.h"
#include "vcd-common-api/vcd-common.h"

#ifdef GDK_WINDOWING_X11
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#endif

#ifndef true
#define true TRUE
#endif

#ifndef false
#define false FALSE
#endif

#ifndef bool
#define bool gint
#endif

GtkWidget *win_toplevel;

static gchar *spice_conf_file;
static gint handler_delete = 0;
static gint handler_window_event_state = 0;
static gboolean error_io = FALSE;

/* 引用channel_display.c中的一个函数修改码率,以及删除正在连接控件 */
extern void spice_display_change_bitrate(int min_bitrate, int max_bitrate);

void vcd_ping_callback(StVcdPingVar *ctx);
/*************************************************************************************************/

typedef struct spice_connection spice_connection;

spice_connection *g_conn = NULL;

enum {
    STATE_SCROLL_LOCK,
    STATE_CAPS_LOCK,
    STATE_NUM_LOCK,
    STATE_MAX,
};

#define SPICE_TYPE_WINDOW                  (spice_window_get_type ())
#define SPICE_WINDOW(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_WINDOW, SpiceWindow))
#define SPICE_IS_WINDOW(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_WINDOW))
#define SPICE_WINDOW_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_WINDOW, SpiceWindowClass))
#define SPICE_IS_WINDOW_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_WINDOW))
#define SPICE_WINDOW_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_WINDOW, SpiceWindowClass))

typedef struct _SpiceWindow SpiceWindow;
typedef struct _SpiceWindowClass SpiceWindowClass;

struct _SpiceWindow {
    GObject          object;
    spice_connection *conn;
    gint             id;
    gint             monitor_id;
    /* change by gt */
    /* 添加一个overlay以便添加悬浮框 */
    GtkWidget        *toplevel, *overlay, *spice;
    GtkWidget        *topbar, *topbar_set_bitrate, *change_display, *net_check, *net_window;
    GtkWidget *item1; //标清
    GtkWidget *item2; //高清
    GtkWidget *item3; //超清
    GtkWidget *item4; //自动
    GtkWidget *message_window;
    /******************/
    GtkWidget        *menubar, *toolbar;
    GtkWidget        *ritem, *rmenu;
    GtkWidget        *statusbar, *status, *st[STATE_MAX];
    GtkActionGroup   *ag;
    GtkUIManager     *ui;
    bool             fullscreen;
    bool             mouse_grabbed;
    SpiceChannel     *display_channel;
    SpicyWindowTimedRevealer *revealer;
#ifdef G_OS_WIN32
    gint             win_x;
    gint             win_y;
#endif
    gboolean         enable_accels_save;
    gboolean         enable_mnemonics_save;

    VcdUsbRedir *vcdUsbRedir;
};

struct _SpiceWindowClass
{
    GObjectClass parent_class;
};

static GType spice_window_get_type(void);

G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT)

#define CHANNELID_MAX 4
#define MONITORID_MAX 4


// FIXME: turn this into an object, get rid of fixed wins array, use
// signals to replace the various callback that iterate over wins array
struct spice_connection {
    SpiceSession     *session;
    SpiceGtkSession  *gtk_session;
    SpiceMainChannel *main;
    SpiceWindow      *wins[CHANNELID_MAX * MONITORID_MAX];
    SpiceAudio       *audio;
    const char       *mouse_state;
    const char       *agent_state;
    gboolean         agent_connected;
    int              disconnecting;

    /* key: SpiceFileTransferTask, value: TransferTaskWidgets */
    GHashTable *transfers;
    GtkWidget  *transfer_dialog;
    GtkWidget  *dialog_logining;
    gpointer    priv; /* pointer of spice_connect_data */
    gboolean    reconnecting_stream;
};
static spice_connection *connection_new(void);
static void connection_connect(spice_connection *conn);
static void connection_disconnect(spice_connection *conn);
static void connection_destroy(SpiceSession *session,
                               spice_connection *conn);
static void usb_connect_failed(GObject               *object,
                               SpiceUsbDevice        *device,
                               GError                *error,
                               gpointer               data);
static gboolean is_gtk_session_property(const gchar *property);
static void del_window(spice_connection *conn, SpiceWindow *win);
static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id);
static GtkWidget* spicy_gtk_image_new_from_name(const char* name);
static void spicy_cancle_login_button_clicked(gpointer p);
static int spicy_on_click_reconnect_vm(gpointer priv);
static void vclient_quit();
/* options */
/* add by gt */
gboolean vcdClient = TRUE;
/**************************************/
static gboolean fullscreen = true;
static gboolean version = false;
static char *spicy_title = NULL;
/* globals */

static GMainLoop *mainloop = NULL;
static int connections = 0;
static GKeyFile *keyfile = NULL;
static SpicePortChannel *stdin_port = NULL;

static void spicy_window_error_io(SpiceWindow *win, const char *message);
/* ------------------------------------------------------------------ */

void spicy_reconnect_begin(void);
void spicy_reconnect_end(void);
gboolean spicy_reconnect_is_connecting(void);
static void _spicy_reconnect_message_box_callback(gpointer p);

static int ask_user(GtkWidget *parent, const char *title, const char *message,
                    char *dest, int dlen, int hide)
{
    GtkWidget *dialog, *area, *label, *entry;
    const char *txt;
    int retval;

    /* Create the widgets */
    dialog = gtk_dialog_new_with_buttons(title,
                                         parent ? GTK_WINDOW(parent) : NULL,
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
                                         "_OK",
                                         GTK_RESPONSE_ACCEPT,
                                         "_Cancel",
                                         GTK_RESPONSE_REJECT,
                                         NULL);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));

    label = gtk_label_new(message);
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    gtk_box_pack_start(GTK_BOX(area), label, FALSE, FALSE, 5);

    entry = gtk_entry_new();
    gtk_entry_set_text(GTK_ENTRY(entry), dest);
    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
    if (hide)
        gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
    gtk_box_pack_start(GTK_BOX(area), entry, FALSE, FALSE, 5);

    /* show and wait for response */
    gtk_widget_show_all(dialog);
    switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
    case GTK_RESPONSE_ACCEPT:
        txt = gtk_entry_get_text(GTK_ENTRY(entry));
        snprintf(dest, dlen, "%s", txt);
        retval = 0;
        break;
    default:
        retval = -1;
        break;
    }
    gtk_widget_destroy(dialog);
    return retval;
}

static void update_status_window(SpiceWindow *win)
{
    GString *status;

    if (win == NULL)
        return;

    status = g_string_new(NULL);
    g_string_printf(status, "mouse: %6s, agent: %3s",
                    win->conn->mouse_state,
                    win->conn->agent_state);

    if (win->mouse_grabbed) {
        SpiceGrabSequence *sequence = spice_display_get_grab_keys(SPICE_DISPLAY(win->spice));
        gchar *seq = spice_grab_sequence_as_string(sequence);
        g_string_append_printf(status, "\tUse %s to ungrab mouse", seq);
        g_free(seq);
    }

    gtk_label_set_text(GTK_LABEL(win->status), status->str);
    g_string_free(status, TRUE);
}

static void update_status(struct spice_connection *conn)
{
    //M_DEBUG("update_status");
    int i;

    for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
        if (conn->wins[i] == NULL)
            continue;
        update_status_window(conn->wins[i]);
    }
}

static const char *spice_edit_properties[] = {
    "CopyToGuest",
    "PasteFromGuest",
};

static void update_edit_menu_window(SpiceWindow *win)
{
    int i;
    GtkAction *toggle;

    if (win == NULL) {
        return;
    }

    /* Make "CopyToGuest" and "PasteFromGuest" insensitive if spice
     * agent is not connected */
    for (i = 0; i < G_N_ELEMENTS(spice_edit_properties); i++) {
        toggle = gtk_action_group_get_action(win->ag, spice_edit_properties[i]);
        if (toggle) {
            gtk_action_set_sensitive(toggle, win->conn->agent_connected);
        }
    }
}

static void update_edit_menu(struct spice_connection *conn)
{
    int i;

    for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
        if (conn->wins[i]) {
            update_edit_menu_window(conn->wins[i]);
        }
    }
}

static void menu_cb_connect(GtkAction *action, void *data)
{
    struct spice_connection *conn;

    conn = connection_new();
    connection_connect(conn);
}

static void menu_cb_close(GtkAction *action, void *data)
{
    SpiceWindow *win = data;
    connection_disconnect(win->conn);
}

static void menu_cb_copy(GtkAction *action, void *data)
{
    SpiceWindow *win = data;

    spice_gtk_session_copy_to_guest(win->conn->gtk_session);
}

static void menu_cb_paste(GtkAction *action, void *data)
{
    SpiceWindow *win = data;

    spice_gtk_session_paste_from_guest(win->conn->gtk_session);
}

static void window_set_fullscreen(SpiceWindow *win, gboolean fs)
{
    if (fs) 
        gtk_window_fullscreen(GTK_WINDOW(win->toplevel));
    else 
        gtk_window_unfullscreen(GTK_WINDOW(win->toplevel));

    win->fullscreen = fs;

    if (!vcdClient) return;

    #ifndef __linux__

//    const char* icon_name = fs ? "toolbar-restore.png" : "toolbar-fullscreen.png";
//    const char* tooltip_text = fs ? "退出全屏" : "全屏";

//    GtkImage* image = (GtkImage*)gtk_tool_button_get_icon_widget(GTK_TOOL_BUTTON(win->change_display));

//    GdkPixbuf* gpb;

//    gpb = gdk_pixbuf_new_from_file(spicy_get_img_in_install_path(icon_name, g_conn->priv),0);
//    //gpb = gdk_pixbuf_scale_simple(gpb,24,24,GDK_INTERP_HYPER);
//    gpb = gdk_pixbuf_scale_simple(gpb,48,48,GDK_INTERP_HYPER);
//    //gtk_image_set_from_file(image,spicy_get_img_in_install_path(icon_name, g_conn->priv));
//    gtk_image_set_from_pixbuf(image,gpb);
//    gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win->change_display), tooltip_text);
//    gdk_pixbuf_unref(gpb);

    if(!fs)
        gdk_window_move_resize(gtk_widget_get_window(win->toplevel),100,100,800,600);
    #endif
}

static void menu_cb_fullscreen(GtkAction *action, void *data)
{
    SpiceWindow *win = data;

    window_set_fullscreen(win, !win->fullscreen);
}

static void menu_cb_show_topbar(GtkAction *action, void *data)
{
    fprintf(stderr, "%s\n", "menu_cb_show_topbar");
}

#ifdef USE_SMARTCARD
static void enable_smartcard_actions(SpiceWindow *win, VReader *reader,
                                     gboolean can_insert, gboolean can_remove)
{
    GtkAction *action;

    if ((reader != NULL) && (!spice_smartcard_reader_is_software((SpiceSmartcardReader*)reader)))
    {
        /* Having menu actions to insert/remove smartcards only makes sense
         * for software smartcard readers, don't do anything when the event
         * we received was for a "real" smartcard reader.
         */
        return;
    }
    action = gtk_action_group_get_action(win->ag, "InsertSmartcard");
    g_return_if_fail(action != NULL);
    gtk_action_set_sensitive(action, can_insert);
    action = gtk_action_group_get_action(win->ag, "RemoveSmartcard");
    g_return_if_fail(action != NULL);
    gtk_action_set_sensitive(action, can_remove);
}


static void reader_added_cb(SpiceSmartcardManager *manager, VReader *reader,
                            gpointer user_data)
{
    enable_smartcard_actions(user_data, reader, TRUE, FALSE);
}

static void reader_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
                              gpointer user_data)
{
    enable_smartcard_actions(user_data, reader, FALSE, FALSE);
}

static void card_inserted_cb(SpiceSmartcardManager *manager, VReader *reader,
                             gpointer user_data)
{
    enable_smartcard_actions(user_data, reader, FALSE, TRUE);
}

static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
                            gpointer user_data)
{
    enable_smartcard_actions(user_data, reader, TRUE, FALSE);
}

static void menu_cb_insert_smartcard(GtkAction *action, void *data)
{
    spice_smartcard_manager_insert_card(spice_smartcard_manager_get());
}

static void menu_cb_remove_smartcard(GtkAction *action, void *data)
{
    spice_smartcard_manager_remove_card(spice_smartcard_manager_get());
}
#endif

static void menu_cb_mouse_mode(GtkAction *action, void *data)
{
    SpiceWindow *win = data;
    SpiceMainChannel *cmain = win->conn->main;
    int mode;

    g_object_get(cmain, "mouse-mode", &mode, NULL);
    if (mode == SPICE_MOUSE_MODE_CLIENT)
        mode = SPICE_MOUSE_MODE_SERVER;
    else
        mode = SPICE_MOUSE_MODE_CLIENT;

    spice_main_channel_request_mouse_mode(cmain, mode);
}

#ifdef USE_USBREDIR
static void remove_cb(GtkContainer *container, GtkWidget *widget, void *data)
{
    gtk_window_resize(GTK_WINDOW(data), 1, 1);
}

static void menu_cb_select_usb_devices(GtkAction *action, void *data)
{
    GtkWidget *dialog, *area, *usb_device_widget;
    SpiceWindow *win = data;

    /* Create the widgets */
    dialog = gtk_dialog_new_with_buttons(
                 "Select USB devices for redirection",
                 GTK_WINDOW(win->toplevel),
                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                 "_Close", GTK_RESPONSE_ACCEPT,
                 NULL);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
    gtk_box_set_spacing(GTK_BOX(gtk_bin_get_child(GTK_BIN(dialog))), 12);

    area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));

    usb_device_widget = spice_usb_device_widget_new(win->conn->session,
                        NULL); /* default format */
    g_signal_connect(usb_device_widget, "connect-failed",
                     G_CALLBACK(usb_connect_failed), NULL);
    gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 0);

    /* This shrinks the dialog when USB devices are unplugged */
    g_signal_connect(usb_device_widget, "remove",
                     G_CALLBACK(remove_cb), dialog);

    /* show and run */
    gtk_widget_show_all(dialog);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}
#endif

static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data)
{
    SpiceWindow *win = data;
    gboolean state = gtk_toggle_action_get_active(action);
    const char *name;
    gpointer object;

    name = gtk_action_get_name(GTK_ACTION(action));
    //M_DEBUG("%s = %s", name, state ? "yes" : "no");

    g_key_file_set_boolean(keyfile, "general", name, state);

    if (is_gtk_session_property(name)) {
        object = win->conn->gtk_session;
    } else {
        object = win->spice;
    }
    g_object_set(object, name, state, NULL);
}

static void menu_cb_conn_bool_prop_changed(GObject    *gobject,
        GParamSpec *pspec,
        gpointer    user_data)
{
    SpiceWindow *win = user_data;
    const gchar *property = g_param_spec_get_name(pspec);
    GtkAction *toggle;
    gboolean state;

    toggle = gtk_action_group_get_action(win->ag, property);
    g_object_get(win->conn->gtk_session, property, &state, NULL);
    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
}

static void menu_cb_toolbar(GtkToggleAction *action, gpointer data)
{
    SpiceWindow *win = data;
    gboolean state = gtk_toggle_action_get_active(action);

    gtk_widget_set_visible(win->toolbar, state);
    g_key_file_set_boolean(keyfile, "ui", "toolbar", state);
}

static void menu_cb_statusbar(GtkToggleAction *action, gpointer data)
{
    SpiceWindow *win = data;
    gboolean state = gtk_toggle_action_get_active(action);

    gtk_widget_set_visible(win->statusbar, state);
    g_key_file_set_boolean(keyfile, "ui", "statusbar", state);
}

static void menu_cb_about(GtkAction *action, void *data)
{
    const char *comments = "gtk test client app for the\n"
                     "spice remote desktop protocol";
    static const char *copyright = "(c) 2010 Red Hat";
    static const char *website = "http://www.spice-space.org";
    static const char *authors[] = { "Gerd Hoffmann <kraxel@redhat.com>",
                                     "Marc-André Lureau <marcandre.lureau@redhat.com>",
                                     NULL
                                   };
    SpiceWindow *win = data;

    gtk_show_about_dialog(GTK_WINDOW(win->toplevel),
                          "authors",         authors,
                          "comments",        comments,
                          "copyright",       copyright,
                          "logo-icon-name",  "help-about",
                          "website",         website,
                          "version",         PACKAGE_VERSION,
                          "license",         "LGPLv2.1",
                          NULL);
}

static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    spicy_reconnect_end();
    SpiceWindow *win = data;

    if (win->monitor_id == 0) {
        connection_disconnect(win->conn);
    } else {
        del_window(win->conn, win);
    }

    return true;
}

static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event,
                                gpointer data)
{
    SpiceWindow *win = data;

    if(event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED){

        if(event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED){
            window_set_fullscreen(win, TRUE);
        }else if (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {

        }

    }

    if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
        win->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;

        /* add by gt */
        /* 如果换成全局变量的窗口，不会调用destroy只会调用hide
           但是hide会触发window-state-event这个信号，所以要做一
           下判断，断开连接的时候不需要做屏幕切换大小信号的处理
           但是此时需要记录一下win->fullscreen*/
        if (vcdClient) {
            if (win->conn->disconnecting)
                return TRUE;
        }
        /****************************/

        if (win->fullscreen) {

            gtk_widget_hide(win->menubar);
            gtk_widget_hide(win->toolbar);
            gtk_widget_hide(win->statusbar);
            //gtk_widget_grab_focus(win->spice);

        } else {

#ifdef __linux__
            if (!vcdClient) {
                gboolean state;
                GtkAction *toggle;

                gtk_widget_show(win->menubar);
                toggle = gtk_action_group_get_action(win->ag, "Toolbar");
                state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
                gtk_widget_set_visible(win->toolbar, state);
                toggle = gtk_action_group_get_action(win->ag, "Statusbar");
                state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
                gtk_widget_set_visible(win->statusbar, state);
            }
#endif
        }
    }
    return TRUE;
}

static void grab_keys_pressed_cb(GtkWidget *widget, gpointer data)
{
    SpiceWindow *win = data;
    //window_set_fullscreen(win, FALSE);

    if(!win || !win->revealer) return;

    GtkWidget* topbar = win->revealer;

    gboolean b = gtk_widget_get_child_visible(topbar);

    gtk_widget_set_child_visible(topbar,!b);
}

static void mouse_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
{
    //M_DEBUG("mouse_grab_cb");
    SpiceWindow *win = data;

    win->mouse_grabbed = grabbed;
    update_status(win->conn);
}

static void keyboard_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
{
    SpiceWindow *win = data;
    GtkSettings *settings = gtk_widget_get_settings (widget);

    if (grabbed) {
        /* disable mnemonics & accels */
        g_object_get(settings,
                     "gtk-enable-accels", &win->enable_accels_save,
                     "gtk-enable-mnemonics", &win->enable_mnemonics_save,
                     NULL);
        g_object_set(settings,
                     "gtk-enable-accels", FALSE,
                     "gtk-enable-mnemonics", FALSE,
                     NULL);
    } else {
        g_object_set(settings,
                     "gtk-enable-accels", win->enable_accels_save,
                     "gtk-enable-mnemonics", win->enable_mnemonics_save,
                     NULL);
    }
}

static void menu_cb_resize_to(GtkAction *action G_GNUC_UNUSED,
                              gpointer data)
{
    SpiceWindow *win = data;
    GtkWidget *dialog;
    GtkWidget *spin_width, *spin_height, *spin_x, *spin_y;
    GtkGrid *grid;
    gint width, height;
    dialog = gtk_dialog_new_with_buttons("Resize guest to",
                                         GTK_WINDOW(win->toplevel),
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
                                         "_Apply",
                                         GTK_RESPONSE_APPLY,
                                         "_Cancel",
                                         GTK_RESPONSE_CANCEL,
                                         NULL);

    spin_width = gtk_spin_button_new_with_range(0, G_MAXINT, 10);
    spin_height = gtk_spin_button_new_with_range(0, G_MAXINT, 10);
    spin_x = gtk_spin_button_new_with_range(0, G_MAXINT, 10);
    spin_y = gtk_spin_button_new_with_range(0, G_MAXINT, 10);

    gtk_widget_get_preferred_width(win->spice, NULL, &width);
    gtk_widget_get_preferred_height(win->spice, NULL, &height);

    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_width), width);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_height), height);

    grid = GTK_GRID(gtk_grid_new());
    gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
                      GTK_WIDGET(grid));
    gtk_grid_attach(grid, gtk_label_new("Resize the guest display:"), 0, 0, 2, 1);
    gtk_grid_attach(grid, gtk_label_new("width:"), 0, 2, 1, 1);
    gtk_grid_attach(grid, spin_width, 1, 2, 1, 1);
    gtk_grid_attach(grid, gtk_label_new("height:"), 0, 3, 1, 1);
    gtk_grid_attach(grid, spin_height, 1, 3, 1, 1);
    gtk_grid_attach(grid, gtk_label_new("x:"), 0, 4, 1, 1);
    gtk_grid_attach(grid, spin_x, 1, 4, 1, 1);
    gtk_grid_attach(grid, gtk_label_new("y:"), 0, 5, 1, 1);
    gtk_grid_attach(grid, spin_y, 1, 5, 1, 1);

    gtk_widget_show_all(dialog);
    if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) {
        spice_main_channel_update_display_enabled(win->conn->main, win->id + win->monitor_id, TRUE,
                FALSE);
        spice_main_channel_update_display(
            win->conn->main,
            win->id + win->monitor_id,
            gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_x)),
            gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_y)),
            gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_width)),
            gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_height)),
            TRUE);
        spice_main_channel_send_monitor_config(win->conn->main);
    }
    gtk_widget_destroy(dialog);
}

static void restore_configuration(SpiceWindow *win)
{
    g_return_if_fail(keyfile != NULL);

    gboolean state;
    gchar *str;
    gchar **keys = NULL;
    gsize nkeys, i;
    GError *error = NULL;
    gpointer object;

    keys = g_key_file_get_keys(keyfile, "general", &nkeys, &error);
    if (error != NULL) {
        if (error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
            g_warning("Failed to read configuration file keys: %s", error->message);
        g_clear_error(&error);
        return;
    }

    if (nkeys > 0)
        g_return_if_fail(keys != NULL);

    for (i = 0; i < nkeys; ++i) {
        if (g_str_equal(keys[i], "grab-sequence"))
            continue;            
        state = g_key_file_get_boolean(keyfile, "general", keys[i], &error);
        M_DEBUG("config general %s", keys[i]);
        if (error != NULL) {
            g_clear_error(&error);
            continue;
        }

        if (is_gtk_session_property(keys[i])) {
            object = win->conn->gtk_session;
        } else {
            object = win->spice;
        }
        g_object_set(object, keys[i], state, NULL);
    }

    /* add by gt*/
    /* 默认使用自动识别分辨率 */
    if (is_gtk_session_property("resize-guest")) {
        object = win->conn->gtk_session;
    } else {
        object = win->spice;
    }
    g_object_set(object, "resize-guest", TRUE, NULL);
    /************************/

    g_strfreev(keys);

    str = g_key_file_get_string(keyfile, "general", "grab-sequence", &error);
    if (error == NULL) {
        SpiceGrabSequence *seq = spice_grab_sequence_new_from_string(str);
        spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
        spice_grab_sequence_free(seq);
        g_free(str);
    }
    g_clear_error(&error);


    state = g_key_file_get_boolean(keyfile, "ui", "toolbar", &error);
    if (error == NULL)
        gtk_widget_set_visible(win->toolbar, state);
    g_clear_error(&error);

    state = g_key_file_get_boolean(keyfile, "ui", "statusbar", &error);
    if (error == NULL)
        gtk_widget_set_visible(win->statusbar, state);
    g_clear_error(&error);
}

/* ------------------------------------------------------------------ */

static const GtkActionEntry entries[] = {
    {
        .name        = "FileMenu",
        .label       = "_File",
    }, {
        .name        = "FileRecentMenu",
        .label       = "_Recent",
    }, {
        .name        = "EditMenu",
        .label       = "_Edit",
    }, {
        .name        = "ViewMenu",
        .label       = "_View",
    }, {
        .name        = "InputMenu",
        .label       = "_Input",
    }, {
        .name        = "OptionMenu",
        .label       = "_Options",
    }, {
        .name        = "CompressionMenu",
        .label       = "_Preferred image compression",
    }, {
        .name        = "VideoCodecTypeMenu",
        .label       = "_Preferred video codec type",
    }, {
        .name        = "HelpMenu",
        .label       = "_Help",
    }, {

        /* File menu */
        .name        = "Connect",
        .stock_id    = "_Connect",
        .label       = "_Connect ...",
        .callback    = G_CALLBACK(menu_cb_connect),
    }, {
        .name        = "Close",
        .stock_id    = "window-close",
        .label       = "_Close",
        .callback    = G_CALLBACK(menu_cb_close),
        .accelerator = "", /* none (disable default "<control>W") */
    }, {

        /* Edit menu */
        .name        = "CopyToGuest",
        .stock_id    = "edit-copy",
        .label       = "_Copy to guest",
        .callback    = G_CALLBACK(menu_cb_copy),
        .accelerator = "", /* none (disable default "<control>C") */
    }, {
        .name        = "PasteFromGuest",
        .stock_id    = "edit-paste",
        .label       = "_Paste from guest",
        .callback    = G_CALLBACK(menu_cb_paste),
        .accelerator = "", /* none (disable default "<control>V") */
    }, {

        /* View menu */
        .name        = "Fullscreen",
        .stock_id    = "view-fullscreen",
        .label       = "_Fullscreen",
        .callback    = G_CALLBACK(menu_cb_fullscreen),
        .accelerator = "<shift>F11",
    }, {
        .name        = "ResizeTo",
        .label       = "_Resize to",
        .callback    = G_CALLBACK(menu_cb_resize_to),
        .accelerator = "",
    }, {
#ifdef USE_SMARTCARD
        .name        = "InsertSmartcard",
        .label       = "_Insert Smartcard",
        .callback    = G_CALLBACK(menu_cb_insert_smartcard),
        .accelerator = "<shift>F8",
    }, {
        .name        = "RemoveSmartcard",
        .label       = "_Remove Smartcard",
        .callback    = G_CALLBACK(menu_cb_remove_smartcard),
        .accelerator = "<shift>F9",
    }, {
#endif

#ifdef USE_USBREDIR
        .name        = "SelectUsbDevices",
        .label       = "_Select USB Devices for redirection",
        .callback    = G_CALLBACK(menu_cb_select_usb_devices),
        .accelerator = "<shift>F10",
    }, {
#endif

        .name        = "MouseMode",
        .label       = "Toggle _mouse mode",
        .callback    = G_CALLBACK(menu_cb_mouse_mode),
        .accelerator = "<shift>F7",

    }, {
        /* Help menu */
        .name        = "About",
        .stock_id    = "help-about",
        .label       = "_About ...",
        .callback    = G_CALLBACK(menu_cb_about),
    }
};

static const char *spice_display_properties[] = {
    "grab-keyboard",
    "grab-mouse",
    "resize-guest",
    "scaling",
    "disable-inputs",
};

static const char *spice_gtk_session_properties[] = {
    "auto-clipboard",
    "auto-usbredir",
    "sync-modifiers",
};

static const GtkToggleActionEntry tentries[] = {
    {
        .name        = "grab-keyboard",
        .label       = "Grab keyboard when active and focused",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "grab-mouse",
        .label       = "Grab mouse in server mode (no tablet/vdagent)",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "resize-guest",
        .label       = "Resize guest to match window size",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "scaling",
        .label       = "Scale display",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "disable-inputs",
        .label       = "Disable inputs",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "sync-modifiers",
        .label       = "Sync modifiers",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "auto-clipboard",
        .label       = "Automatic clipboard sharing between host and guest",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "auto-usbredir",
        .label       = "Auto redirect newly plugged in USB devices",
        .callback    = G_CALLBACK(menu_cb_bool_prop),
    }, {
        .name        = "Statusbar",
        .label       = "Statusbar",
        .callback    = G_CALLBACK(menu_cb_statusbar),
    }, {
        .name        = "Toolbar",
        .label       = "Toolbar",
        .callback    = G_CALLBACK(menu_cb_toolbar),
    }
};

static const GtkRadioActionEntry compression_entries[] = {
    {
        .name  = "auto-glz",
        .label = "auto-glz",
        .value = SPICE_IMAGE_COMPRESSION_AUTO_GLZ,
    }, {
        .name  = "auto-lz",
        .label = "auto-lz",
        .value = SPICE_IMAGE_COMPRESSION_AUTO_LZ,
    }, {
        .name  = "quic",
        .label = "quic",
        .value = SPICE_IMAGE_COMPRESSION_QUIC,
    }, {
        .name  = "glz",
        .label = "glz",
        .value = SPICE_IMAGE_COMPRESSION_GLZ,
    }, {
        .name  = "lz",
        .label = "lz",
        .value = SPICE_IMAGE_COMPRESSION_LZ,
    }, {
#ifdef USE_LZ4
        .name  = "lz4",
        .label = "lz4",
        .value = SPICE_IMAGE_COMPRESSION_LZ4,
    }, {
#endif
        .name  = "off",
        .label = "off",
        .value = SPICE_IMAGE_COMPRESSION_OFF,
    }
};

static const GtkRadioActionEntry video_codec_type_entries[] = {
    {
        .name  = "mjpeg",
        .label = "mjpeg",
        .value = SPICE_VIDEO_CODEC_TYPE_MJPEG,
    }, {
        .name  = "vp8",
        .label = "vp8",
        .value = SPICE_VIDEO_CODEC_TYPE_VP8,
    }, {
        .name  = "vp9",
        .label = "vp9",
        .value = SPICE_VIDEO_CODEC_TYPE_VP9,
    }, {
        .name  = "h264",
        .label = "h264",
        .value = SPICE_VIDEO_CODEC_TYPE_H264,
    }
};

static char ui_xml[] =
    "<ui>\n"
    "  <menubar action='MainMenu'>\n"
    "    <menu action='FileMenu'>\n"
    "      <menuitem action='Connect'/>\n"
    "      <menu action='FileRecentMenu'/>\n"
    "      <separator/>\n"
    "      <menuitem action='Close'/>\n"
    "    </menu>\n"
    "    <menu action='EditMenu'>\n"
    "      <menuitem action='CopyToGuest'/>\n"
    "      <menuitem action='PasteFromGuest'/>\n"
    "    </menu>\n"
    "    <menu action='ViewMenu'>\n"
    "      <menuitem action='Fullscreen'/>\n"
    "      <menuitem action='Toolbar'/>\n"
    "      <menuitem action='Statusbar'/>\n"
    "    </menu>\n"
    "    <menu action='InputMenu'>\n"
#ifdef USE_SMARTCARD
    "      <menuitem action='InsertSmartcard'/>\n"
    "      <menuitem action='RemoveSmartcard'/>\n"
#endif
#ifdef USE_USBREDIR
    "      <menuitem action='SelectUsbDevices'/>\n"
#endif
    "    </menu>\n"
    "    <menu action='OptionMenu'>\n"
    "      <menuitem action='grab-keyboard'/>\n"
    "      <menuitem action='grab-mouse'/>\n"
    "      <menuitem action='MouseMode'/>\n"
    "      <menuitem action='resize-guest'/>\n"
    "      <menuitem action='scaling'/>\n"
    "      <menuitem action='disable-inputs'/>\n"
    "      <menuitem action='sync-modifiers'/>\n"
    "      <menuitem action='auto-clipboard'/>\n"
    "      <menuitem action='auto-usbredir'/>\n"
    "      <menu action='CompressionMenu'>\n"
    "        <menuitem action='auto-glz'/>\n"
    "        <menuitem action='auto-lz'/>\n"
    "        <menuitem action='quic'/>\n"
    "        <menuitem action='glz'/>\n"
    "        <menuitem action='lz'/>\n"
#ifdef USE_LZ4
    "        <menuitem action='lz4'/>\n"
#endif
    "        <menuitem action='off'/>\n"
    "      </menu>\n"
    "      <menu action='VideoCodecTypeMenu'>\n"
    "        <menuitem action='mjpeg'/>\n"
    "        <menuitem action='vp8'/>\n"
    "        <menuitem action='vp9'/>\n"
    "        <menuitem action='h264'/>\n"
    "      </menu>\n"
    "    </menu>\n"
    "    <menu action='HelpMenu'>\n"
    "      <menuitem action='About'/>\n"
    "    </menu>\n"
    "  </menubar>\n"
    "  <toolbar action='ToolBar'>\n"
    "    <toolitem action='Close'/>\n"
    "    <separator/>\n"
    "    <toolitem action='CopyToGuest'/>\n"
    "    <toolitem action='PasteFromGuest'/>\n"
    "    <separator/>\n"
    "    <toolitem action='Fullscreen'/>\n"
    "    <separator/>\n"
    "    <toolitem action='ResizeTo'/>\n"
    "    <separator/>\n"
    "  </toolbar>\n"
    "</ui>\n";

static gboolean is_gtk_session_property(const gchar *property)
{
    int i;

    for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
        if (!strcmp(spice_gtk_session_properties[i], property)) {
            return TRUE;
        }
    }
    return FALSE;
}

static void recent_item_activated_cb(GtkRecentChooser *chooser, gpointer data)
{
    GtkRecentInfo *info;
    struct spice_connection *conn;
    const char *uri;

    info = gtk_recent_chooser_get_current_item(chooser);

    uri = gtk_recent_info_get_uri(info);
    g_return_if_fail(uri != NULL);

    conn = connection_new();
    g_object_set(conn->session, "uri", uri, NULL);
    gtk_recent_info_unref(info);
    connection_connect(conn);
}

static void compression_cb(GtkRadioAction *action G_GNUC_UNUSED,
                           GtkRadioAction *current,
                           gpointer user_data)
{
    spice_display_channel_change_preferred_compression(SPICE_CHANNEL(user_data),
            gtk_radio_action_get_current_value(current));
}

static void video_codec_type_cb(GtkRadioAction *action G_GNUC_UNUSED,
                                GtkRadioAction *current,
                                gpointer user_data)
{
    spice_display_channel_change_preferred_video_codec_type(SPICE_CHANNEL(user_data),
            gtk_radio_action_get_current_value(current));
}

static void
spice_window_class_init (SpiceWindowClass *klass)
{
}

static void
spice_window_init (SpiceWindow *self)
{
}

/* add by gt */
/* 断网重连系列函数 */
static void del_message_window(SpiceWindow *win)
{
    if (win->message_window) {
        M_DEBUG("delete reconnect message box");
        gtk_widget_destroy(win->message_window);
        win->message_window = NULL;
    }
}

static void spicy_window_error_io_callback_close(SpiceWindow *win)
{
    M_DEBUG("window_error_io_callback_close");
    del_message_window(win);
    connection_disconnect(win->conn);
}

static void spicy_window_error_io_callback_reconnect(SpiceWindow *win)
{
    M_DEBUG("window_error_io_callback_reconnect");
    del_message_window(win);
    connection_disconnect(win->conn);
    //auto_connect_vm();
    get_record_data_and_connect();
}

/*********************/

/* add by gt */
/* 悬浮框系列函数 */
static void spicy_window_quit(GtkWidget *src G_GNUC_UNUSED, SpiceWindow *win)
{
    M_DEBUG("click window quit");
    connection_disconnect(win->conn);
}

static int
get_bitrate_mode(void)
{
    g_return_val_if_fail(g_vcd_conf!=NULL,0);
    //reload_vcd_conf();
    return g_vcd_conf->bitrate;
}

static void
set_bitrate_mode(int mode)
{
    g_return_if_fail(g_vcd_conf != NULL);
    struct vcd_conf* f =  g_vcd_conf;
    f->bitrate = mode;
    update_vcd_conf();
}

static void
spicy_menu_set_bitrate1(GtkWidget *menu, SpiceWindow *priv)
{
    g_return_if_fail(g_vcd_conf != NULL);

    struct vcd_conf* f =  g_vcd_conf;

    gtk_tool_button_set_label(GTK_TOOL_BUTTON(priv->topbar_set_bitrate), "标清");

    if (f->login_way == PUBLIC_LOGIN) {
        spice_display_change_bitrate(1, 3);
    }
    else {
        spice_display_change_bitrate(1, 5);
    }
    set_bitrate_mode(1);
}

static void
spicy_menu_set_bitrate2(GtkWidget *menu, SpiceWindow *priv)
{
    g_return_if_fail(g_vcd_conf != NULL);
    struct vcd_conf* f =  g_vcd_conf;
    
    gtk_tool_button_set_label(GTK_TOOL_BUTTON(priv->topbar_set_bitrate), "高清");

    if (f->login_way == PUBLIC_LOGIN) {
        spice_display_change_bitrate(4, 6);
    }
    else {
        spice_display_change_bitrate(6, 12);
    }
    set_bitrate_mode(2);
}

static void
spicy_menu_set_bitrate3(GtkWidget *menu, SpiceWindow *priv)
{
    g_return_if_fail(g_vcd_conf != NULL);
    struct vcd_conf* f =  g_vcd_conf;

    gtk_tool_button_set_label(GTK_TOOL_BUTTON(priv->topbar_set_bitrate), "超清");

    if (f->login_way == PUBLIC_LOGIN) {
        spice_display_change_bitrate(7, 10);
    }
    else {
        spice_display_change_bitrate(13, 20);
    }
    set_bitrate_mode(3);
}

static void
spicy_menu_set_bitrate4(GtkWidget *menu, SpiceWindow *priv)
{
    g_return_if_fail(g_vcd_conf != NULL);
    struct vcd_conf* f =  g_vcd_conf;

    gtk_tool_button_set_label(GTK_TOOL_BUTTON(priv->topbar_set_bitrate), "自动");

    if (f->login_way == PUBLIC_LOGIN) {
        spice_display_change_bitrate(0, 10);
    }
    else {
        spice_display_change_bitrate(0, 30);
    }
    set_bitrate_mode(4);
}

static GtkMenu*
spicy_window_get_bitrate_menu(SpiceWindow *win)
{
    GtkMenu *menu = GTK_MENU(gtk_menu_new());

    win->item1 = gtk_menu_item_new_with_mnemonic("标清");
    win->item2 = gtk_menu_item_new_with_mnemonic("高清");
    win->item3 = gtk_menu_item_new_with_mnemonic("超清");
    win->item4 = gtk_menu_item_new_with_mnemonic("自动");

    g_signal_connect(win->item1, "activate", G_CALLBACK(spicy_menu_set_bitrate1), win);
    g_signal_connect(win->item2, "activate", G_CALLBACK(spicy_menu_set_bitrate2), win);
    g_signal_connect(win->item3, "activate", G_CALLBACK(spicy_menu_set_bitrate3), win);
    g_signal_connect(win->item4, "activate", G_CALLBACK(spicy_menu_set_bitrate4), win);

    gtk_container_add(GTK_CONTAINER(menu), win->item4);
    gtk_container_add(GTK_CONTAINER(menu), win->item1);
    gtk_container_add(GTK_CONTAINER(menu), win->item2);
    gtk_container_add(GTK_CONTAINER(menu), win->item3);

    gtk_widget_show_all(GTK_WIDGET(menu));

    return menu;
}

static void spicy_window_error_io(SpiceWindow *win, const char *msg)
{
    if (!win) {
        M_DEBUG("window_error_io SWindow = NULL");
        return;
    }
    if (win->message_window) {
        M_DEBUG("swindow_error_io was already reported");
        return;
    }
    GtkWidget *grid, *hbox, *labelnone, *labelnone2, *labelnone3, *label, *label2, *btnyes, *btnno;
    win->message_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_keep_above(GTK_WINDOW(win->message_window), TRUE);
    gtk_window_set_modal((GtkWindow *)win->message_window, TRUE);
    gtk_window_set_position(GTK_WINDOW(win->message_window), GTK_WIN_POS_CENTER_ALWAYS);//居中
    gtk_container_set_border_width(GTK_CONTAINER(win->message_window), 35);
    gtk_window_set_decorated(GTK_WINDOW(win->message_window), FALSE); //无边框
    gtk_window_set_transient_for(GTK_WINDOW(win->message_window), GTK_WINDOW(win->toplevel));

    grid = gtk_grid_new();

    label = gtk_image_new_from_file(vcd_common_get_absolute_img_path("wenhao.png"));
    labelnone = gtk_label_new("                           ");
    labelnone2 = gtk_label_new("                           ");
    labelnone3 = gtk_label_new(NULL);
    if (msg) {
        label2 = gtk_label_new(msg);
        M_DEBUG(msg);
    } else {
        label2 = gtk_label_new("\n网络异常，是否尝试重连？\n");
        M_DEBUG("网络异常，是否尝试重连？");
    }
    btnyes = gtk_button_new_with_label("确定");
    btnno = gtk_button_new_with_label("取消");
    gtk_button_set_relief(GTK_BUTTON(btnno), GTK_RELIEF_NONE);
    gtk_button_set_relief(GTK_BUTTON(btnyes), GTK_RELIEF_NONE);

    gtk_widget_set_size_request(label, 70, 70);

    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
    gtk_container_add(GTK_CONTAINER(hbox), labelnone);
    gtk_container_add(GTK_CONTAINER(hbox), label);
    gtk_container_add(GTK_CONTAINER(hbox), labelnone2);

    gtk_grid_set_row_spacing((GtkGrid*)grid, 5);
    gtk_grid_set_column_spacing((GtkGrid*)grid, 10);

    gtk_grid_attach((GtkGrid *)grid, labelnone3, 0, 0, 4, 1);
    gtk_grid_attach((GtkGrid *)grid, hbox, 0, 1, 4, 1);
    gtk_grid_attach((GtkGrid *)grid, label2, 1, 2, 2, 1);
    gtk_grid_attach((GtkGrid *)grid, btnyes, 1, 3, 1, 1);
    gtk_grid_attach((GtkGrid *)grid, btnno, 2, 3, 1, 1);

    gtk_container_add(GTK_CONTAINER(win->message_window), grid);

    g_signal_connect_swapped(G_OBJECT(btnyes), "clicked", G_CALLBACK(spicy_window_error_io_callback_reconnect), win);
    g_signal_connect_swapped(G_OBJECT(btnno), "clicked", G_CALLBACK(spicy_window_error_io_callback_close), win);

    /*            CSS                  */
    //set_img(label, "label", "wenhao.png");
    set_css(btnyes, "button{color: white;background: rgb(53, 126, 169);border: 1px solid rgb(40, 94, 142);border-radius: 0px}");
    set_css(btnyes, "button:hover{border-radius: 0px;background: #3070a9}");
    set_css(btnno, "button{color: white;background: #5cb85c;border: 1px solid rgb(57, 132, 57);border-radius: 0px}");
    set_css(btnno, "button:hover{border-radius:0px;background: #449d44;}");
    set_css(win->message_window, "window{border-radius:0px;background-color: rgb(255, 255, 255)}");
    /*            CSS                 */
    gtk_widget_show_all(win->message_window);

}

static void bitrate_menu_location(GtkMenu *menu G_GNUC_UNUSED, gint *x, gint *y,
                                  gboolean *push_in, gpointer user_data)
{
    SpiceWindow *self = user_data;
    GtkAllocation allocation;
    GtkWidget *toplevel = gtk_widget_get_toplevel(self->topbar_set_bitrate);

    *push_in = TRUE;
    gdk_window_get_origin(gtk_widget_get_window(toplevel), x, y);
    gtk_widget_translate_coordinates(self->topbar_set_bitrate, toplevel,
                                     *x, *y, x, y);
    gtk_widget_get_allocation(self->topbar_set_bitrate, &allocation);
    *y += allocation.height;
}

static void
spicy_topbar_set_bitrate(GtkWidget *src G_GNUC_UNUSED, SpiceWindow *win)
{
    GtkMenu *menu = spicy_window_get_bitrate_menu(win);
    gtk_menu_attach_to_widget(menu, win->toplevel, NULL);
    g_object_ref_sink(menu);
    gtk_menu_popup(menu, NULL, NULL, bitrate_menu_location, win,
                   0, gtk_get_current_event_time());
    g_object_unref(menu);
}


static void spicy_topbar_check_net(GtkWidget *src G_GNUC_UNUSED, SpiceWindow *win)
{
    if (!win->net_window) {

        win->net_window = vcd_net_window_new(win->overlay);
    }

    if(!gtk_widget_is_visible(win->net_window)){
        gtk_widget_show(win->net_window);

    }else{
        gtk_widget_hide(win->net_window);

    }
}

 static void _on_clicked_toolbar_usb(GtkWidget* widget,gpointer priv){
    SpiceWindow *win = (SpiceWindow *)priv;
    if(win && win->vcdUsbRedir)
        vcd_usb_redir(0,win->vcdUsbRedir);
 }

 static void _on_clicked_toolbar_reconnect(GtkWidget* widget,gpointer priv){
    SpiceWindow *win = (SpiceWindow *)priv;
 }

 static void _on_clicked_toolbar_close(GtkWidget* widget,gpointer priv){
    SpiceWindow *win = (SpiceWindow *)priv;
    if(win)
        connection_disconnect(win->conn);
 }

 static void _on_clicked_toolbar_fullscreen(GtkWidget* widget,gpointer priv){

    SpiceWindow *win = (SpiceWindow *)priv;
    if(win)
        window_set_fullscreen(win, !win->fullscreen);
 }

/************************************************************/

void set_window_style(GtkWidget* topLevel){
#ifndef __linux__
    // set title
    gtk_window_set_title(GTK_WINDOW(topLevel), " Vclient");

    #if 0
    // set icon
    vcd_common_set_app_icon(topLevel,VCD_PIC_PATH"Vclient.ico");
    #else
    vcd_common_set_app_icon(topLevel,spicy_get_img_in_install_path("Vclient.png", g_conn->priv));
    #endif
#endif
}

static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
{
    char title[32];
    SpiceWindow *win;
    GtkAction *toggle;
    gboolean state;
    GtkWidget *vbox, *frame;

    GError *err = NULL;
    int i;
    SpiceGrabSequence *seq;

    win = g_object_new(SPICE_TYPE_WINDOW, NULL);
    win->id = id;
    win->monitor_id = monitor_id;
    win->conn = conn;
    win->display_channel = channel;

    /* toplevel */
    /* change by gt */
    /* 窗口变成全局便变量，win->toplevel和全局变量指向同一个地方,这样修改地方最少 */
    if ( /*FALSE &&*/ vcdClient) {
        if (!win_toplevel) {
            win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
            win_toplevel = win->toplevel;
        } else {
            win->toplevel = win_toplevel;
        }
    } else {
        win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    }
    /******************************/

    if (spicy_title == NULL) {
        snprintf(title, sizeof(title), "spice display %d:%d", id, monitor_id);
    } else {
        snprintf(title, sizeof(title), "%s", spicy_title);
    }
    gtk_window_set_title(GTK_WINDOW(win->toplevel), title);

    /* change by gt */
    /* 添加信号的句柄，因为此时不删除窗口，每次只是hide这个窗口，
       窗口之前绑定的信号还存在会影响下一次连接时绑定信号，因此
       每次绑定信号之前会先删除一下窗口绑定的信号 */
    if (vcdClient) {
        set_window_style(win->toplevel);

        if (handler_window_event_state)
            g_signal_handler_disconnect(G_OBJECT(win->toplevel), handler_window_event_state);
        handler_window_event_state = g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
                                     G_CALLBACK(window_state_cb), win);
        if (handler_delete)
            g_signal_handler_disconnect(G_OBJECT(win->toplevel), handler_delete);
        handler_delete = g_signal_connect(G_OBJECT(win->toplevel), "delete-event",
                                          G_CALLBACK(delete_cb), win);
    } else {
        g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
                         G_CALLBACK(window_state_cb), win);
        g_signal_connect(G_OBJECT(win->toplevel), "delete-event",
                         G_CALLBACK(delete_cb), win);
    }
    /******************************/

    /* menu + toolbar */
    win->ui = gtk_ui_manager_new();
    win->ag = gtk_action_group_new("MenuActions");
    gtk_action_group_add_actions(win->ag, entries, G_N_ELEMENTS(entries), win);
    gtk_action_group_add_toggle_actions(win->ag, tentries,
                                        G_N_ELEMENTS(tentries), win);
    gtk_action_group_add_radio_actions(win->ag, compression_entries,
                                       G_N_ELEMENTS(compression_entries), -1,
                                       G_CALLBACK(compression_cb), win->display_channel);
    if (!spice_channel_test_capability(win->display_channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION)) {
        GtkAction *compression_menu_action = gtk_action_group_get_action(win->ag, "CompressionMenu");
        gtk_action_set_sensitive(compression_menu_action, FALSE);
    }
    gtk_action_group_add_radio_actions(win->ag, video_codec_type_entries,
                                       G_N_ELEMENTS(video_codec_type_entries), SPICE_VIDEO_CODEC_TYPE_H264,
                                       G_CALLBACK(video_codec_type_cb), win->display_channel);
    if (!spice_channel_test_capability(win->display_channel,
                                       SPICE_DISPLAY_CAP_PREF_VIDEO_CODEC_TYPE)) {
        GtkAction *video_codec_type_menu_action =
            gtk_action_group_get_action(win->ag, "VideoCodecTypeMenu");
        gtk_action_set_sensitive(video_codec_type_menu_action, FALSE);
    }
    // set SPICE_VIDEO_CODEC_TYPE_H264 preferred video codec type
    else{
        spice_display_channel_change_preferred_video_codec_type(SPICE_CHANNEL(win->display_channel),SPICE_VIDEO_CODEC_TYPE_H264);
    }

    gtk_ui_manager_insert_action_group(win->ui, win->ag, 0);
    gtk_window_add_accel_group(GTK_WINDOW(win->toplevel),
                               gtk_ui_manager_get_accel_group(win->ui));

    err = NULL;
    if (!gtk_ui_manager_add_ui_from_string(win->ui, ui_xml, -1, &err)) {
        g_warning("building menus failed: %s", err->message);
        g_error_free(err);
        exit(1);
    }
    win->menubar = gtk_ui_manager_get_widget(win->ui, "/MainMenu");
    win->toolbar = gtk_ui_manager_get_widget(win->ui, "/ToolBar");

    /* recent menu */
    win->ritem  = gtk_ui_manager_get_widget
                  (win->ui, "/MainMenu/FileMenu/FileRecentMenu");

    GtkRecentFilter  *rfilter;

    win->rmenu = gtk_recent_chooser_menu_new();
    gtk_recent_chooser_set_show_icons(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
    rfilter = gtk_recent_filter_new();
    gtk_recent_filter_add_mime_type(rfilter, "application/x-spice");
    gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(win->rmenu), rfilter);
    gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->ritem), win->rmenu);
    g_signal_connect(win->rmenu, "item-activated",
                     G_CALLBACK(recent_item_activated_cb), win);

    /* spice display */
    win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
    //seq = spice_grab_sequence_new_from_string("Shift_L+F12");
    seq = spice_grab_sequence_new_from_string("Control_L+Shift_L+F12");
    spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
    spice_grab_sequence_free(seq);

    g_signal_connect(G_OBJECT(win->spice), "mouse-grab",
                     G_CALLBACK(mouse_grab_cb), win);
    g_signal_connect(G_OBJECT(win->spice), "keyboard-grab",
                     G_CALLBACK(keyboard_grab_cb), win);

    g_signal_connect(G_OBJECT(win->spice), "grab-keys-pressed",
                     G_CALLBACK(grab_keys_pressed_cb), win);
    /* status line */
    win->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);

    win->status = gtk_label_new("status line");
    gtk_misc_set_alignment(GTK_MISC(win->status), 0, 0.5);
    gtk_misc_set_padding(GTK_MISC(win->status), 3, 1);
    update_status_window(win);

    frame = gtk_frame_new(NULL);
    gtk_box_pack_start(GTK_BOX(win->statusbar), frame, TRUE, TRUE, 0);
    gtk_container_add(GTK_CONTAINER(frame), win->status);

    for (i = 0; i < STATE_MAX; i++) {
        win->st[i] = gtk_label_new("?");
        gtk_label_set_width_chars(GTK_LABEL(win->st[i]), 5);
        frame = gtk_frame_new(NULL);
        gtk_box_pack_end(GTK_BOX(win->statusbar), frame, FALSE, FALSE, 0);
        gtk_container_add(GTK_CONTAINER(frame), win->st[i]);
    }

#ifdef USE_USBREDIR
    win->vcdUsbRedir = create_vcd_usb_redir(win->conn->session, win->toplevel);
#endif

    /* add by gt */
    if (vcdClient) {
        win->overlay = gtk_overlay_new();//为了添加顶上的悬浮框

        TopbarPostion position = POSITION_TOP;
        gboolean is_show_topbar = TRUE;
        if(g_vcd_conf){
            position = g_vcd_conf->topbar_position;
            is_show_topbar = g_vcd_conf->is_show_topbar;
        }

        VcdTopbarCtx* context = g_malloc0(sizeof(VcdTopbarCtx));
        context->is_show_topbar = is_show_topbar;
        context->position = position;
        context->on_fullscreen = _on_clicked_toolbar_fullscreen;
        context->on_close = _on_clicked_toolbar_close;
        context->on_reconnect = _on_clicked_toolbar_reconnect;
        context->on_usb_redir = _on_clicked_toolbar_usb;
        context->priv = win;

        GtkWidget* topbar_box = vcd_topbar_get_buttons(context);
        //win->revealer = spicy_window_timed_revealer_new(topbar_box,position);
        win->revealer = topbar_box;
    }
    /******************************************************/

    /* Make a vbox and put stuff in */
    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);

    /* change by gt */
    /* window和vbox中间加了一层overlay，来显示悬浮框，将vbox放在overlay上 */
    if (vcdClient) {
        gtk_container_add(GTK_CONTAINER(win->overlay), vbox);
        gtk_overlay_add_overlay(GTK_OVERLAY(win->overlay), GTK_WIDGET(win->revealer));

        gtk_container_add(GTK_CONTAINER(win->toplevel), win->overlay);
    } else {
        gtk_container_add(GTK_CONTAINER(win->toplevel), vbox);
    }

    gtk_box_pack_start(GTK_BOX(vbox), win->menubar, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), win->toolbar, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), win->spice, TRUE, TRUE, 0);
    gtk_box_pack_end(GTK_BOX(vbox), win->statusbar, FALSE, TRUE, 0);

    /* change by gt */
    /* 直接显示overlay(之前是直接显示vbox)) */
    if (vcdClient)
        gtk_widget_show_all(win->overlay);
    else
        gtk_widget_show_all(vbox);

    restore_configuration(win);

    /* init toggle actions */
    for (i = 0; i < G_N_ELEMENTS(spice_display_properties); i++) {
        toggle = gtk_action_group_get_action(win->ag,
                                             spice_display_properties[i]);
        g_object_get(win->spice, spice_display_properties[i], &state, NULL);
        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
    }

    for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
        char notify[64];

        toggle = gtk_action_group_get_action(win->ag,
                                             spice_gtk_session_properties[i]);
        g_object_get(win->conn->gtk_session, spice_gtk_session_properties[i],
                     &state, NULL);
        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);

        snprintf(notify, sizeof(notify), "notify::%s",
                 spice_gtk_session_properties[i]);
        spice_g_signal_connect_object(win->conn->gtk_session, notify,
                                      G_CALLBACK(menu_cb_conn_bool_prop_changed),
                                      win, 0);
    }

    update_edit_menu_window(win);

    toggle = gtk_action_group_get_action(win->ag, "Toolbar");
    state = gtk_widget_get_visible(win->toolbar);
    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);

    toggle = gtk_action_group_get_action(win->ag, "Statusbar");
    state = gtk_widget_get_visible(win->statusbar);
    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);

#ifdef USE_SMARTCARD
    gboolean smartcard;

    enable_smartcard_actions(win, NULL, FALSE, FALSE);
    g_object_get(G_OBJECT(conn->session),
                 "enable-smartcard", &smartcard,
                 NULL);
    if (smartcard) {
        g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-added",
                         (GCallback)reader_added_cb, win);
        g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-removed",
                         (GCallback)reader_removed_cb, win);
        g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-inserted",
                         (GCallback)card_inserted_cb, win);
        g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-removed",
                         (GCallback)card_removed_cb, win);
    }
#endif

#ifndef USE_USBREDIR
    GtkAction *usbredir = gtk_action_group_get_action(win->ag, "auto-usbredir");
    gtk_action_set_visible(usbredir, FALSE);
#endif

    //gtk_widget_grab_focus(win->spice);

#if 0
    /* add by gt */
    if (vcdClient) {
        int mode = get_bitrate_mode();
        if (mode == 1) {
            spicy_menu_set_bitrate1(win->item1, win);
        }
        else if (mode == 2) {
            spicy_menu_set_bitrate2(win->item2, win);
        }
        else if (mode == 3) {
            spicy_menu_set_bitrate3(win->item3, win);
        }
        else if (mode == 4) {
            spicy_menu_set_bitrate4(win->item4, win);
        }
        else {}
    }
#endif

    return win;
}

static void destroy_spice_window(SpiceWindow *win)
{
    if (win == NULL)
        return;
   
    if (handler_window_event_state) {
        g_signal_handler_disconnect(G_OBJECT(win->toplevel), handler_window_event_state);
        handler_window_event_state = 0;
    }
        
    if (handler_delete) {
        g_signal_handler_disconnect(G_OBJECT(win->toplevel), handler_delete);
        handler_delete = 0;
    }

    //M_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
    g_object_unref(win->ag);
    g_object_unref(win->ui);
    /* change by gt */
    /* 使用全局变量window句柄每次只是hide不退出，销毁前先销毁子窗口 */
    if (vcdClient) {
        if (!spicy_reconnect_is_connecting()) {
            gtk_widget_destroy(win->overlay);
            gtk_widget_hide(win->toplevel);
            #ifndef __linux__
            gtk_widget_destroy(win->toplevel);
            win_toplevel = NULL;
            #endif
            g_object_unref(win);
        } else {
            win_toplevel = NULL;
            g_reconnect.win = win;
        }
    } else {
        gtk_widget_destroy(win->toplevel);
        g_object_unref(win);
    }
}

/* ------------------------------------------------------------------ */

static void recent_add(SpiceSession *session)
{
    GtkRecentManager *recent;
    GtkRecentData meta = {
        .mime_type    = (char*)"application/x-spice",
        .app_name     = (char*)"spicy",
        .app_exec     = (char*)"spicy --uri=%u",
    };
    char *uri;

    g_object_get(session, "uri", &uri, NULL);
    //M_DEBUG("%s: %s", __FUNCTION__, uri);

    recent = gtk_recent_manager_get_default();
    if (g_str_has_prefix(uri, "spice://"))
        meta.display_name = uri + 8;
    else if (g_str_has_prefix(uri, "spice+unix://"))
        meta.display_name = uri + 13;
    else
        g_return_if_reached();

    if (!gtk_recent_manager_add_full(recent, uri, &meta))
        g_warning("Recent item couldn't be added successfully");

    g_free(uri);
}

static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
                               gpointer data)
{
    const GError *error = NULL;
    spice_connection *conn = data;
    int rc;

    gint channel_type;
    g_object_get(channel,"channel-type", &channel_type,NULL);
    const char* str_type = spice_channel_type_to_string(channel_type);

    switch (event) {
    case SPICE_CHANNEL_OPENED:
         if(SPICE_IS_MAIN_CHANNEL(channel)){
            g_message("main channel: opened");
            M_INFO("main channel opened");
        }
        break;
    case SPICE_CHANNEL_SWITCHING:
        g_message("CHANNEL_SWITCHING channel:%s",str_type);
        break;
    case SPICE_CHANNEL_CLOSED:
            
        if(SPICE_IS_MAIN_CHANNEL(channel)){
            if(g_conn && vcdClient){
                M_ERROR("[%s] channel closed",str_type);
                connection_disconnect(conn);

            }else{
                M_ERROR("[%s] channel closed not reconnect",str_type);
            }
        }
        break;
    case SPICE_CHANNEL_ERROR_IO:{
            g_message("CHANNEL_ERROR_IO channel: %s",str_type);
            M_DEBUG("CHANNEL_ERROR_IO channel: %s",str_type);
                
            /* change by gt */
            if (vcdClient) {
                if (conn->disconnecting) {
                    M_DEBUG("have error io but conn->disconnecting = true return!");
                    break;
                }

            }
            connection_disconnect(conn);
        }
        /*****************/
        break;
    case SPICE_CHANNEL_ERROR_TLS:
    case SPICE_CHANNEL_ERROR_LINK:
    case SPICE_CHANNEL_ERROR_CONNECT:
        error = spice_channel_get_error(channel);
        g_message("CHANNEL_ERROR_* channel:%s",str_type);
        if (error) {
            g_message("channel error: %s", error->message);
            M_ERROR("channel error: %s", error->message);
        }

        /* add by gt */
        if (vcdClient) {
            M_DEBUG("CHANNEL_ERROR_* channel:%s",str_type);
            if (error) {
                gchar szbuf[255];
                snprintf(szbuf, sizeof(szbuf), "IO超时,网络异常或者端口等配置有误 %s", error->message);
                M_ERROR("channel error: %s", szbuf);
                message_box(szbuf);
            }
        }

        connection_disconnect(conn);

        /*******************************/
        break;
    case SPICE_CHANNEL_ERROR_AUTH:
        g_warning("[channel:%s] CHANNEL_ERROR_AUTH auth failure (wrong password?)",str_type);
        M_ERROR("[channel:%s] CHANNEL_ERROR_AUTH auth failure (wrong password?)",str_type);
        connection_disconnect(conn);

        break;
    case SPICE_CHANNEL_OTHER_CLIENT_LOGIN:
        M_ERROR("CHANNEL_OTHER_CLIENT_LOGIN channel:%s",str_type);
        g_reconnect.status = ReconOtherClientLogin;
        break;
    default:
        /* TODO: more sophisticated error handling */
        g_warning("unknown main channel event: %u", event);
        /* connection_disconnect(conn); */
        break;
    }
}

static void main_mouse_update(SpiceChannel *channel, gpointer data)
{
    //M_DEBUG("main_mouse_update");
    spice_connection *conn = data;
    gint mode;

    g_object_get(channel, "mouse-mode", &mode, NULL);
    switch (mode) {
    case SPICE_MOUSE_MODE_SERVER:
        conn->mouse_state = "server";
        break;
    case SPICE_MOUSE_MODE_CLIENT:
        conn->mouse_state = "client";
        break;
    default:
        conn->mouse_state = "?";
        break;
    }
    update_status(conn);
}

static void main_agent_update(SpiceChannel *channel, gpointer data)
{
    //M_DEBUG("main_agent_update");
    spice_connection *conn = data;

    g_object_get(channel, "agent-connected", &conn->agent_connected, NULL);
    conn->agent_state = conn->agent_connected ? "yes" : "no";
    update_status(conn);
    update_edit_menu(conn);
}

static void inputs_modifiers(SpiceChannel *channel, gpointer data)
{
    spice_connection *conn = data;
    int m, i;

    g_object_get(channel, "key-modifiers", &m, NULL);
    for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
        if (conn->wins[i] == NULL)
            continue;

        gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_SCROLL_LOCK]),
                           m & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK ? "SCROLL" : "");
        gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_CAPS_LOCK]),
                           m & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK ? "CAPS" : "");
        gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_NUM_LOCK]),
                           m & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK ? "NUM" : "");
    }
}

static void display_mark(SpiceChannel *channel, gint mark, SpiceWindow *win)
{
    g_return_if_fail(win != NULL);
    g_return_if_fail(win->toplevel != NULL);

    int id;
    g_object_get(channel, "channel-id", &id, NULL);
    
    //M_DEBUG("display_mark channel-id:%d, mark:%d",id,mark);

    if (mark == TRUE) {

        gtk_widget_show(win->toplevel);

        // destory dialog_logining
        if (g_conn && g_conn->dialog_logining) {
            gtk_widget_destroy(g_conn->dialog_logining);
            g_conn->dialog_logining = NULL;
            //M_DEBUG("destory dialog_logining");
         }
        
    } else {
        gtk_widget_hide(win->toplevel);
    }
}

static void update_auto_usbredir_sensitive(spice_connection *conn)
{
#ifdef USE_USBREDIR
    int i;
    GtkAction *ac;
    gboolean sensitive;

    sensitive = spice_session_has_channel_type(conn->session,
                SPICE_CHANNEL_USBREDIR);
    for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
        if (conn->wins[i] == NULL)
            continue;
        ac = gtk_action_group_get_action(conn->wins[i]->ag, "auto-usbredir");
        gtk_action_set_sensitive(ac, sensitive);
    }
#endif
}

static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
{
    g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
    g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);

    return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
}

static void add_window(spice_connection *conn, SpiceWindow *win)
{
    g_return_if_fail(win != NULL);
    g_return_if_fail(win->id < CHANNELID_MAX);
    g_return_if_fail(win->monitor_id < MONITORID_MAX);
    g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);

    //M_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
    conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
}

static void del_window(spice_connection *conn, SpiceWindow *win)
{
    //M_DEBUG("del_window");
    if (win == NULL)
        return;

    if (vcdClient) {
        if (win->net_window) {
            gtk_widget_destroy(win->net_window);
            win->net_window = NULL;
        }
    }

    g_return_if_fail(win->id < CHANNELID_MAX);
    g_return_if_fail(win->monitor_id < MONITORID_MAX);
    M_DEBUG("del display monitor %d:%d", win->id, win->monitor_id);
    conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
    if (win->id > 0)
        spice_main_channel_update_display_enabled(conn->main, win->id, FALSE, TRUE);
    else
        spice_main_channel_update_display_enabled(conn->main, win->monitor_id, FALSE, TRUE);
    //针对win10作出的修改删除下面这句话不然win10连不上
    //spice_main_channel_send_monitor_config(conn->main);

    destroy_spice_window(win);
}

static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
                             spice_connection *conn)
{
    GArray *monitors = NULL;
    int id;
    guint i;

    g_object_get(display,
                 "channel-id", &id,
                 "monitors", &monitors,
                 NULL);
    g_return_if_fail(monitors != NULL);

#if 1
    if(id > 0){
        M_DEBUG("display_monitors id :%d > 0 return",id);
        return;
    }
#endif

    for (i = 0; i < monitors->len; i++) {
        SpiceWindow *w;

        if (!get_window(conn, id, i)) {

            w = create_spice_window(conn, display, id, i);
            add_window(conn, w);
            spice_g_signal_connect_object(display, "display-mark",
                                          G_CALLBACK(display_mark), w, 0);
            gtk_widget_show(w->toplevel);
            update_auto_usbredir_sensitive(conn);

            /* show window */
            if (fullscreen) {
                window_set_fullscreen(w,fullscreen);
            }
        }
    }

    for (; i < MONITORID_MAX; i++)
        del_window(conn, get_window(conn, id, i));

    g_clear_pointer(&monitors, g_array_unref);
}

static void port_write_cb(GObject *source_object,
                          GAsyncResult *res,
                          gpointer user_data)
{
    SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
    GError *error = NULL;

    spice_port_channel_write_finish(port, res, &error);
    if (error != NULL)
        g_warning("%s", error->message);
    g_clear_error(&error);
}

static void port_flushed_cb(GObject *source_object,
                            GAsyncResult *res,
                            gpointer user_data)
{
    SpiceChannel *channel = SPICE_CHANNEL(source_object);
    GError *error = NULL;

    spice_channel_flush_finish(channel, res, &error);
    if (error != NULL)
        g_warning("%s", error->message);
    g_clear_error(&error);

    spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED);
}

static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
{
    char buf[4096];
    gsize bytes_read;
    GIOStatus status;

    if (!(condition & G_IO_IN))
        return FALSE;

    status = g_io_channel_read_chars(gin, buf, sizeof(buf), &bytes_read, NULL);
    if (status != G_IO_STATUS_NORMAL)
        return FALSE;

    if (stdin_port != NULL)
        spice_port_channel_write_async(stdin_port, buf, bytes_read, NULL, port_write_cb, NULL);

    return TRUE;
}

static void watch_stdin(void);

static void port_opened(SpiceChannel *channel, GParamSpec *pspec,
                        spice_connection *conn)
{
    SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
    gchar *name = NULL;
    gboolean opened = FALSE;

    g_object_get(channel,
                 "port-name", &name,
                 "port-opened", &opened,
                 NULL);

    g_printerr("port %p %s: %s\n", channel, name, opened ? "opened" : "closed");

    if (opened) {
        /* only send a break event and disconnect */
        if (g_strcmp0(name, "org.spice.spicy.break") == 0) {
            spice_port_channel_event(port, SPICE_PORT_EVENT_BREAK);
            spice_channel_flush_async(channel, NULL, port_flushed_cb, conn);
        }

        /* handle the first spicy port and connect it to stdin/out */
        if (g_strcmp0(name, "org.spice.spicy") == 0 && stdin_port == NULL) {
            watch_stdin();
            stdin_port = port;
        }
    } else {
        if (port == stdin_port)
            stdin_port = NULL;
    }

    g_free(name);
}

static void port_data(SpicePortChannel *port,
                      gpointer data, int size, spice_connection *conn)
{
    int r;

    if (port != stdin_port)
        return;

    r = write(fileno(stdout), data, size);
    if (r != size) {
        g_warning("port write failed result %d/%d errno %d", r, size, errno);
    }
}

typedef struct {
    GtkWidget *vbox;
    GtkWidget *hbox;
    GtkWidget *progress;
    GtkWidget *label;
    GtkWidget *cancel;
} TransferTaskWidgets;

static void transfer_update_progress(GObject *object,
                                     GParamSpec *pspec,
                                     gpointer user_data)
{
    spice_connection *conn = user_data;
    TransferTaskWidgets *widgets = g_hash_table_lookup(conn->transfers, object);
    g_return_if_fail(widgets);
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(widgets->progress),
                                  spice_file_transfer_task_get_progress(SPICE_FILE_TRANSFER_TASK(object)));
}

static void transfer_task_finished(SpiceFileTransferTask *task, GError *error, spice_connection *conn)
{
    if (error)
        g_warning("%s", error->message);
    g_hash_table_remove(conn->transfers, task);
    if (!g_hash_table_size(conn->transfers))
        gtk_widget_hide(conn->transfer_dialog);
}

static gboolean dialog_delete_cb(GtkWidget *widget,
                                 GdkEvent *event G_GNUC_UNUSED,
                                 gpointer user_data G_GNUC_UNUSED)
{
    gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL);
    return TRUE;
}

static void dialog_response_cb(GtkDialog *dialog,
                               gint response_id,
                               gpointer user_data)
{
    spice_connection *conn = user_data;
    g_print("Reponse: %i\n", response_id);

    if (response_id == GTK_RESPONSE_CANCEL) {
        GHashTableIter iter;
        gpointer key, value;

        g_hash_table_iter_init(&iter, conn->transfers);
        while (g_hash_table_iter_next(&iter, &key, &value)) {
            SpiceFileTransferTask *task = key;
            spice_file_transfer_task_cancel(task);
        }
    }
}

static void
task_cancel_cb(GtkButton *button,
               gpointer user_data)
{
    SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(user_data);
    spice_file_transfer_task_cancel(task);
}

static TransferTaskWidgets *
transfer_task_widgets_new(SpiceFileTransferTask *task)
{
    char *filename;
    TransferTaskWidgets *widgets = g_new0(TransferTaskWidgets, 1);

    widgets->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    widgets->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
    widgets->cancel = gtk_button_new_with_label("Cancel");

    widgets->progress = gtk_progress_bar_new();
    filename = spice_file_transfer_task_get_filename(task);
    widgets->label = gtk_label_new(filename);
    g_free(filename);

    gtk_widget_set_halign(widgets->label, GTK_ALIGN_START);
    gtk_widget_set_valign(widgets->label, GTK_ALIGN_BASELINE);
    gtk_widget_set_valign(widgets->progress, GTK_ALIGN_CENTER);
    gtk_widget_set_hexpand(widgets->progress, TRUE);
    gtk_widget_set_valign(widgets->cancel, GTK_ALIGN_CENTER);
    gtk_widget_set_hexpand(widgets->progress, FALSE);

    gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->progress,
                       TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->cancel,
                       FALSE, TRUE, 0);

    gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->label,
                       TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->hbox,
                       TRUE, TRUE, 0);

    g_signal_connect(widgets->cancel, "clicked",
                     G_CALLBACK(task_cancel_cb), task);

    gtk_widget_show_all(widgets->vbox);

    return widgets;
}

static void
transfer_task_widgets_free(TransferTaskWidgets *widgets)
{
    /* child widgets will be destroyed automatically */
    gtk_widget_destroy(widgets->vbox);
    g_free(widgets);
}

static void spice_connection_add_task(spice_connection *conn, SpiceFileTransferTask *task)
{
    TransferTaskWidgets *widgets;
    GtkWidget *content = NULL;

    g_signal_connect(task, "notify::progress",
                     G_CALLBACK(transfer_update_progress), conn);
    g_signal_connect(task, "finished",
                     G_CALLBACK(transfer_task_finished), conn);
    if (!conn->transfer_dialog) {
        conn->transfer_dialog = gtk_dialog_new_with_buttons("File Transfers",
                                GTK_WINDOW(conn->wins[0]->toplevel), 0,
                                "Cancel", GTK_RESPONSE_CANCEL, NULL);
        gtk_dialog_set_default_response(GTK_DIALOG(conn->transfer_dialog),
                                        GTK_RESPONSE_CANCEL);
        gtk_window_set_resizable(GTK_WINDOW(conn->transfer_dialog), FALSE);
        g_signal_connect(conn->transfer_dialog, "response",
                         G_CALLBACK(dialog_response_cb), conn);
        g_signal_connect(conn->transfer_dialog, "delete-event",
                         G_CALLBACK(dialog_delete_cb), conn);
    }
    gtk_widget_show(conn->transfer_dialog);
    content = gtk_dialog_get_content_area(GTK_DIALOG(conn->transfer_dialog));
    gtk_container_set_border_width(GTK_CONTAINER(content), 12);

    widgets = transfer_task_widgets_new(task);
    g_hash_table_insert(conn->transfers, g_object_ref(task), widgets);
    gtk_box_pack_start(GTK_BOX(content),
                       widgets->vbox, TRUE, TRUE, 6);
}

static void new_file_transfer(SpiceMainChannel *main, SpiceFileTransferTask *task, gpointer user_data)
{
    spice_connection *conn = user_data;
    g_debug("new file transfer task");
    spice_connection_add_task(conn, task);
}
static void primary_create(SpiceChannel* channel,gint format,gint width,gint height,gint stride,gint shmid,gpointer imgdata,gpointer data){

    //printf("primary_create w:%d h:%d\n",width,height);
    vcd_net_window_set_screen_size(width,height);
}
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
{
    spice_connection *conn = data;
    int id;

    g_object_get(channel, "channel-id", &id, NULL);

    //conn->channels++;
    //M_DEBUG("new channel (#%d), channel nums (%d)", id, conn->channels);

    if (SPICE_IS_MAIN_CHANNEL(channel)) {
        M_DEBUG("new main channel");
        conn->main = SPICE_MAIN_CHANNEL(channel);
        g_signal_connect(channel, "channel-event",
                         G_CALLBACK(main_channel_event), conn);
        g_signal_connect(channel, "main-mouse-update",
                         G_CALLBACK(main_mouse_update), conn);
        g_signal_connect(channel, "main-agent-update",
                         G_CALLBACK(main_agent_update), conn);
        g_signal_connect(channel, "new-file-transfer",
                         G_CALLBACK(new_file_transfer), conn);
        main_mouse_update(channel, conn);
        main_agent_update(channel, conn);
    }

    if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
        if (id >= SPICE_N_ELEMENTS(conn->wins))
            return;
        if (conn->wins[id] != NULL)
            return;
        M_DEBUG("new display channel (#%d)", id);
        g_signal_connect(channel, "notify::monitors",
                         G_CALLBACK(display_monitors), conn);

        g_signal_connect(channel, "display-primary-create",
                         G_CALLBACK(primary_create), conn);

        spice_channel_connect(channel);
    }

    if (SPICE_IS_INPUTS_CHANNEL(channel)) {
        M_DEBUG("new inputs channel");
        g_signal_connect(channel, "inputs-modifiers",
                         G_CALLBACK(inputs_modifiers), conn);
    }

    if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
        M_DEBUG("new audio channel");
        conn->audio = spice_audio_get(s, NULL);
    }

    if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
        M_DEBUG("new usbredir channel");
        update_auto_usbredir_sensitive(conn);
    }

    if (SPICE_IS_PORT_CHANNEL(channel)) {
        M_DEBUG("new port channel");
        g_signal_connect(channel, "notify::port-opened",
                         G_CALLBACK(port_opened), conn);
        g_signal_connect(channel, "port-data",
                         G_CALLBACK(port_data), conn);
        spice_channel_connect(channel);
    }
}

static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data)
{
    spice_connection *conn = data;
    int id;
    g_object_get(channel, "channel-id", &id, NULL);
    if (SPICE_IS_MAIN_CHANNEL(channel)) {
        M_DEBUG("zap main channel");
        conn->main = NULL;
    }

    if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
        if (id >= SPICE_N_ELEMENTS(conn->wins))
            return;
        M_DEBUG("zap display channel (#%d)", id);
        /* FIXME destroy widget only */
    }

    if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
        M_DEBUG("zap audio channel");
    }

    if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
        update_auto_usbredir_sensitive(conn);
    }

    if (SPICE_IS_PORT_CHANNEL(channel)) {
        if (SPICE_PORT_CHANNEL(channel) == stdin_port)
            stdin_port = NULL;
    }
}

static void migration_state(GObject *session,
                            GParamSpec *pspec, gpointer data)
{
    SpiceSessionMigration mig;

    g_object_get(session, "migration-state", &mig, NULL);
    if (mig == SPICE_SESSION_MIGRATION_SWITCHING)
        g_message("migrating session");
}

static spice_connection *connection_new(void)
{
    spice_connection *conn;
    SpiceUsbDeviceManager *manager;

    conn = g_new0(spice_connection, 1);
    conn->session = spice_session_new();
    conn->gtk_session = spice_gtk_session_get(conn->session);
    g_signal_connect(conn->session, "channel-new",
                     G_CALLBACK(channel_new), conn);
    g_signal_connect(conn->session, "channel-destroy",
                     G_CALLBACK(channel_destroy), conn);
    g_signal_connect(conn->session, "notify::migration-state",
                     G_CALLBACK(migration_state), conn);
    g_signal_connect(conn->session, "disconnected",
                     G_CALLBACK(connection_destroy), conn);

    manager = spice_usb_device_manager_get(conn->session, NULL);
    if (manager) {
        g_signal_connect(manager, "auto-connect-failed",
                         G_CALLBACK(usb_connect_failed), NULL);
        g_signal_connect(manager, "device-error",
                         G_CALLBACK(usb_connect_failed), NULL);
    }

    conn->transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                            g_object_unref,
                                            (GDestroyNotify)transfer_task_widgets_free);
    connections++;
    SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
    return conn;
}

static void connection_connect(spice_connection *conn)
{
    M_DEBUG("connection_connect disconnecting:%d", conn->disconnecting);
    conn->disconnecting = false;
    spice_session_connect(conn->session);
}

static void connection_disconnect(spice_connection *conn)
{
    M_DEBUG("connection disconnect");
    if (conn->disconnecting)
        return;

    conn->disconnecting = true;
    g_conn = NULL;
    /* add by gt */
    if (vcdClient) {

        del_window(conn, conn->wins[0]);
#ifdef USE_USBREDIR
        destroy_vcd_usb_redir();
#endif
    }
    /******************/
    M_DEBUG("spice_session_disconnect");
    spice_session_disconnect(conn->session);

    vclient_quit();
}

static void connection_destroy(SpiceSession *session,
                               spice_connection *conn)
{
    g_object_unref(conn->session);
    g_hash_table_unref(conn->transfers);
    g_free(conn);

    connections--;
    M_DEBUG("%s (%d)", __FUNCTION__, connections);
    if (connections > 0)
        return;

    /* change by gt */
    /* 若使用官方界面直接退出主函数,我们的界面记录虚拟机连接
       使用的配置文件并发信息给管理平台告知虚拟机连接断开 */
    if (vcdClient) {

        /*此处由于有时候调用不到导致，进程不退出，所以搬到别的地方去了
            vclient_quit();
        */
    } else {
        g_main_loop_quit(mainloop);
    }
    /***************************************/
}

/* ------------------------------------------------------------------ */

static GOptionEntry cmd_entries[] = {
    {
        .long_name        = "full-screen",
        .short_name       = 'f',
        .arg              = G_OPTION_ARG_NONE,
        .arg_data         = &fullscreen,
        .description      = "Open in full screen mode",
    }, {
        .long_name        = "vcd-client",
        .short_name       = 'c',
        .arg              = G_OPTION_ARG_NONE,
        .arg_data         = &vcdClient,
        .description      = "use verycloud client",
    }, {
        .long_name        = "version",
        .arg              = G_OPTION_ARG_NONE,
        .arg_data         = &version,
        .description      = "Display version and quit",
    }, {
        .long_name        = "title",
        .arg              = G_OPTION_ARG_STRING,
        .arg_data         = &spicy_title,
        .description      = "Set the window title",
        .arg_description  = "<title>",
    }, {
        /* end of list */
    }
};

static void usb_connect_failed(GObject               *object,
                               SpiceUsbDevice        *device,
                               GError                *error,
                               gpointer               data)
{
    GtkWidget *dialog;

    if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
        return;

    dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
                                    GTK_BUTTONS_CLOSE,
                                    "USB redirection error");
    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
            "%s", error->message);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}

static void setup_terminal(gboolean reset)
{
    int stdinfd = fileno(stdin);

    if (!isatty(stdinfd))
        return;

#ifdef HAVE_TERMIOS_H
    struct termios tios;
    static struct termios saved_tios;
    static bool saved = false;

    if (reset) {
        if (!saved)
            return;
        tios = saved_tios;
    } else {
        tcgetattr(stdinfd, &tios);
        saved_tios = tios;
        saved = true;
        tios.c_lflag &= ~(ICANON | ECHO);
    }

    tcsetattr(stdinfd, TCSANOW, &tios);
#endif
}

static void watch_stdin(void)
{
    int stdinfd = fileno(stdin);
    GIOChannel *gin;

    setup_terminal(false);
    gin = g_io_channel_unix_new(stdinfd);
    g_io_channel_set_flags(gin, G_IO_FLAG_NONBLOCK, NULL);
    g_io_add_watch(gin, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, input_cb, NULL);
}

/* change by gt */
/*
    这是原始的spicy的main函数 将optionContext(argc, argv)
    这个分离到vcd-client.c中的新的主函数中，因为我们需要在
    新的主函数中通过这个函数知道到底使用我们的客户端界面还
    是官方界面 (gtk_init gst_init gst_deinit也在新的主函数中)
*/
void main_origin(int argc, char *argv[])
{
    M_DEBUG("origin main");
    GError *error = NULL;
    spice_connection *conn;
    gchar *conf_file, *conf;
    char *host = NULL, *port = NULL, *tls_port = NULL, *unix_path = NULL;

    keyfile = g_key_file_new();

    int mode = S_IRWXU;
    conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);
    if (g_mkdir_with_parents(conf_file, mode) == -1)
        M_DEBUG("failed to create config directory");
    g_free(conf_file);

    conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL);
    if (!g_key_file_load_from_file(keyfile, conf_file,
                                   G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
        M_DEBUG("Couldn't load configuration: %s", error->message);
        g_clear_error(&error);
    }
/*
    gtk_init(&argc, &argv);
    gst_init(&argc, &argv);
    context = g_option_context_new("- spice client test application");
    g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers.");
    g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
    g_option_context_add_group(context, spice_get_option_group());
    g_option_context_set_main_group(context, spice_cmdline_get_option_group());
    g_option_context_add_main_entries(context, cmd_entries, NULL);
    g_option_context_add_group(context, gtk_get_option_group(TRUE));
    g_option_context_add_group(context, gst_init_get_option_group());
    if (!g_option_context_parse (context, &argc, &argv, &error)) {
        g_print("option parsing failed: %s\n", error->message);
        exit(1);
    }
    g_option_context_free(context);

    if (version) {
        g_print("spicy " PACKAGE_VERSION "\n");
        exit(0);
    }
*/
    mainloop = g_main_loop_new(NULL, false);

    conn = connection_new();
    spice_set_session_option(conn->session);
    spice_cmdline_session_setup(conn->session);

    g_object_get(conn->session,
                 "unix-path", &unix_path,
                 "host", &host,
                 "port", &port,
                 "tls-port", &tls_port,
                 NULL);
    /* If user doesn't provide hostname and port, show the dialog window
       instead of connecting to server automatically */
    //官方版本为什么一定要经过这段代码才会显示正常的虚拟机呢？？否则就是黑屏！！！必须要界面填写ip端口，写命令行参数不行！！！
    if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
        if (!spicy_connect_dialog(conn->session)) {
            exit(0);
        }
    }
    
    g_free(host);
    g_free(port);
    g_free(tls_port);
    g_free(unix_path);

    // g_object_set(conn->session, "host", "192.168.112.4", NULL);
    // g_object_set(conn->session, "port", "61000", NULL);

    connection_connect(conn);

    if (connections > 0)
        g_main_loop_run(mainloop);
    g_main_loop_unref(mainloop);

    if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL ||
            !g_file_set_contents(conf_file, conf, -1, &error)) {
        M_DEBUG("Couldn't save configuration: %s", error->message);
        g_error_free(error);
        error = NULL;
    }

    g_free(conf_file);
    g_free(conf);
    g_key_file_free(keyfile);

    g_free(spicy_title);

    setup_terminal(true);
}
/******************/

/* add by gt */
/* vcd-client获取到连接虚拟机的参数后传入这个函数执行连接指令 */
void spicy_connect(const char *host, const char *port, const char *password, const char *proxy)
{
    read_connect_config_file();// read vm config file

    M_INFO("connect host:%s, port:%s, password:%s, proxy:%s", host, port, password, proxy);

    spice_connection *conn;
    conn = connection_new();
    spice_set_session_option(conn->session);
    spice_cmdline_session_setup(conn->session);

    /* 将参数设置给session */
    g_object_set(conn->session, "host", host, NULL);
    g_object_set(conn->session, "port", port, NULL);
    g_object_set(conn->session, "password", password, NULL);
    g_object_set(conn->session, "proxy", proxy, NULL);

    /* 初始化usb模块 */
#ifdef USE_USBREDIR
    if (vcdClient) {
        M_DEBUG("init_usb_redir_operate_callback");
        init_usb_redir_operate_callback(conn->session, host, port);
    }
#endif
    connection_connect(conn);//连接
    g_conn = conn;
}

void spicy_disconnect(void)
{
    if (g_conn) {
        connection_disconnect(g_conn);
        g_conn = NULL;
    }
}

/* 分离出来的虚拟机连接需要的配置文件 */
void read_connect_config_file(void)
{
    GError *error = NULL;

    keyfile = g_key_file_new();

    int mode = S_IRWXU;
    spice_conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);
    if (g_mkdir_with_parents(spice_conf_file, mode) == -1)
        LOG_ERROR("failed to create config directory");
    g_free(spice_conf_file);

    spice_conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL);
    if (!g_key_file_load_from_file(keyfile, spice_conf_file,
                                   G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
        LOG_ERROR("Couldn't load configuration: %s", error->message);
        g_clear_error(&error);
    }
}

/* 通过返回参数中获取到的的vcdClient全局变量判断使用我们的界面还是官方界面 */
gboolean optionContext(int argc, char *argv[])
{
    GError *error = NULL;

    GOptionContext *context;

    context = g_option_context_new("- spice client test application");
    g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers.");
    g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
    g_option_context_add_group(context, spice_get_option_group());
    g_option_context_set_main_group(context, spice_cmdline_get_option_group());
    g_option_context_add_main_entries(context, cmd_entries, NULL);
    g_option_context_add_group(context, gtk_get_option_group(TRUE));
#if HAVE_GSTAUDIO || HAVE_GSTVIDEO
    //g_option_context_add_group(context, gst_init_get_option_group());
#endif
    if (!g_option_context_parse (context, &argc, &argv, &error)) {
        g_print("option parsing failed: %s\n", error->message);
        //exit(1);
    }
    g_option_context_free(context);

    if (version) {
        g_print("spicy " PACKAGE_VERSION "\n");
        exit(0);
    }

#ifndef __linux__
    vcdClient = true;
#endif
    return vcdClient;
}

/******************************/
// reconnect when channel close

gboolean _timer_func_reconnect(void)
{
    M_DEBUG("_timer_func_reconnect");
    if(g_reconnect.status >= ReconVmUnStart ){
        M_DEBUG("_timer_func_reconnect g_reconnect.status:%d",g_reconnect.status);
        spicy_reconnect_end();
        vcd_logined_window_show();
        return FALSE;
    }
    get_record_data_and_connect();
    return TRUE;
}

void spicy_reconnect_begin(void){
    #ifdef HAVE_RECONNECT
    g_message("reconnect_begin");
    if(spicy_reconnect_is_connecting()) return;

    g_reconnect.is_connecting = TRUE;
    g_reconnect.timer = g_timeout_add_seconds(2,(GSourceFunc)_timer_func_reconnect, NULL);

    g_reconnect.message_box = message_box_reconnect("网络异常,尝试重连...",_spicy_reconnect_message_box_callback);
    #else
    M_DEBUG("reconnect not support,use --enable-reconnect");
    #endif
}

void spicy_reconnect_end(void){
    #ifdef HAVE_RECONNECT
    //g_message("spicy_reconnect_end");
    g_reconnect.is_connecting = FALSE;

    if(g_reconnect.timer){
        g_source_remove(g_reconnect.timer);
        g_reconnect.timer = 0;
    }

    if(g_reconnect.win){
        SpiceWindow * win = g_reconnect.win;
        gtk_widget_destroy(win->overlay);
        gtk_widget_hide(win->toplevel);
        //#ifndef __linux__
        gtk_widget_destroy(win->toplevel);
        //#endif
        g_object_unref(win);
        g_reconnect.win = 0;
    }

    if(g_reconnect.message_box){
        gtk_widget_destroy(GTK_WIDGET(g_reconnect.message_box));
        g_reconnect.message_box = 0;
    }

    vcd_com_reconnect_set_status(ReconDefault);
    #endif
}

gboolean spicy_reconnect_is_connecting(void){

    return g_reconnect.is_connecting;
}

static void _spicy_reconnect_message_box_callback(gpointer p) {
    M_DEBUG("reconnect_message_box_callback");

    if (spicy_reconnect_is_connecting()) {
        spicy_reconnect_end();
        vcd_logined_window_show();
    } else {
        gtk_widget_destroy(GTK_WIDGET(p));
    }
}

void spicy_connect_with_priv(const char *host, const char *port, const char *password, const char *proxy, gpointer priv) {
    spicy_connect(host, port, password, proxy);
    g_conn->priv = priv;

   // 设置正在登录窗口
    if (spicy_get_img_in_install_path("loading300x250.gif", priv))
        g_conn->dialog_logining = vcd_message_dialog_logining_with_gif(spicy_get_img_in_install_path("loading300x250.gif", priv), spicy_cancle_login_button_clicked);
    else
        g_conn->dialog_logining = vcd_message_dialog_logining();
    gtk_main();
}

char* spicy_get_img_in_install_path(const char* name, spice_connect_data* scd){

    char icon_path[1000] = {0};

#ifdef __linux__
    //linux 串流+spice
    sprintf(icon_path,"%s%s",VCD_PIC_PATH,name);
#else

    if (!scd || !scd->install_path) return NULL;
    sprintf(icon_path, "%s/share/img/%s", scd->install_path, name);

#endif
    // 未做内存释放
    return g_canonicalize_filename(icon_path, NULL);
}

static GtkWidget* spicy_gtk_image_new_from_name(const char* name){

    GdkPixbuf* gpb;
    GtkWidget* image;

    gpb = gdk_pixbuf_new_from_file(spicy_get_img_in_install_path(name, g_conn ? g_conn->priv : 0),0);
    gpb = gdk_pixbuf_scale_simple(gpb,24,24,GDK_INTERP_HYPER);

    image = gtk_image_new_from_pixbuf(gpb);
    return image;
}

static void spicy_cancle_login_button_clicked(gpointer p) {

    M_DEBUG("clicked cancle button");
    spicy_disconnect();
}

static void spicy_reconnect_vm(gpointer priv){

    M_DEBUG("clicked reconnect vm button");
    spice_connection* conn = (spice_connection*)priv;
    if (!conn) return;

    //此处未实现，因为去除了重连的功能
}

static int spicy_on_click_reconnect_vm(gpointer priv)
{
    spicy_reconnect_vm(priv);
//    M_DEBUG("reboot vm");
//    message_diag_context *t = g_malloc0(sizeof(message_diag_context));
//    g_return_val_if_fail(t != NULL, -1);
//    t->btn_yes = spicy_reconnect_vm;
//    t->btn_no = NULL;
//    t->info = "您将重新连接虚拟机,是否继续?";
//    t->user_data = priv;
//    vcd_message_dialog(t);
//    g_free(t);
//    t = NULL;
    return 0;
}

static void vclient_quit()
{
    // if is local-model,not gtk_main_quit()
    if(g_vcd_conf && g_vcd_conf->local_model_check_status){
        return;
    }

    GError *error = NULL;
    gchar *conf;
    M_DEBUG("connection_destroy:record vm config");
    if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL ||
            !g_file_set_contents(spice_conf_file, conf, -1, &error)) {
        M_DEBUG("Couldn't save configuration: %s", error ? error->message : "");
        g_error_free(error);
        error = NULL;
    }

    g_free(spice_conf_file);
    g_free(conf);
    g_key_file_free(keyfile);
    g_free(spicy_title);

    //send disconnect signal to gateway
    tell_gateway_client_disconnect();

    //join the process to send disconnect
    stop_download_thread();

    M_INFO("vclient quit");
    gtk_main_quit();
}

void spicy_connect_with_unix(const char *unix_path) {

    M_INFO("connect unix_sock:%s", unix_path);

    if(!keyfile)
        keyfile = g_key_file_new();

    spice_connection *conn;
    conn = connection_new();
    spice_set_session_option(conn->session);
    spice_cmdline_session_setup(conn->session);

    /* 将参数设置给session */
    g_object_set(conn->session, "unix-path", unix_path, NULL);

    /* 初始化usb模块 */
    M_DEBUG("init_usb_redir_operate_callback");
    init_usb_redir_operate_callback(conn->session, "", "0");

    connection_connect(conn);//连接
    g_conn = conn;

   // 设置正在登录窗口
    if (spicy_get_img_in_install_path("loading300x250.gif", 0))
        g_conn->dialog_logining = vcd_message_dialog_logining_with_gif(spicy_get_img_in_install_path("loading300x250.gif", 0), spicy_cancle_login_button_clicked);
    else
        g_conn->dialog_logining = vcd_message_dialog_logining();
}

void vcd_spice_start_sync(spice_connect_data *scd)
{
    const char* host = scd->host;
    const char* port = scd->port;
    const char* pass = scd->password;
    const char* proxy = scd->proxy;

    check_is_process_exist("verystream.exe", TRUE);
    spicy_connect_with_priv(host, port, pass, proxy, scd);
}
